#include "Core\Oatmeal.h"			// standard library include (must be first include)
#include "Core\Internals.h"			// standard include for intenral definitions
#include "Core\System.h"			// system identification/manipulation routines

////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////// SYSTEM IDENTIFICATION/MANIPULATION ROUTINES ////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

// private routines
static OS _GetOSVersion (void);

// don't forget supporting big/little endian stuff

////////////////////////////////////////////////////////////////////////////////////////////////////

/*/
/ / uint16
/ /		apiGetCPUCount (void)
/ /
/ / PURPOSE:
/ /		Retrieves the number of *logical* processors for the host environment.
/ /
/ / RETURNS:
/ /		Assumes one CPU on error, as the system should have at least one.
/*/

OATINLINE(uint16)
apiGetCPUCount (void)
{
	uint16 nReturn = 1;

#if defined(_WIN32)

	SYSTEM_INFO info = {0};

	// we don't care about the WOW64 layer
	GetSystemInfo(&info);
	nReturn = (uint16)info.dwNumberOfProcessors;
#endif

	// linux/unix way
	//sysconf(_SC_NPROCESSORS_ONLN)
	///proc/cpuinfo

	return nReturn;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

/*/
/ / uint64
/ /		oatGetMemoryAmount (void)
/ /
/ / PURPOSE:
/ /		Retrieves the amount of system memory (in megabytes) for the host environment.
/ /
/ / RETURNS:
/ /		Returns zero on error, or the amount of physical memory in bytes on success.
/*/

OATAPI(uint64)
oatGetMemoryAmount (void)
{
	uint64 nReturn = 0;

#if defined(_WIN32)
	OS osVersion = _GetOSVersion();
	SYSTEM_INFO info = {0};
	GetSystemInfo(&info);

	// for versions of Windows that can exceed 4GB memory, use Ex to avoid an overflow
	if(osVersion >= Win2000)
	{
		MEMORYSTATUSEX mem = {0};
		mem.dwLength = sizeof(mem);
		GlobalMemoryStatusEx(&mem);
		nReturn = mem.ullTotalPhys;
	}
	else
	{
		MEMORYSTATUS mem = {0};
		mem.dwLength = sizeof(mem);
		GlobalMemoryStatus(&mem);
		nReturn = mem.dwTotalPhys;

		// thanks to the geniuses at Redmond, GlobalMemoryStatus() usually returns bogus info
		// because it's an estimate of PFN x an old page size, we attempt to fix the number first
		// by going to the next multiple of 32MB (assumes the system has at least that much memory)
		nReturn = (uint32)(ceil((float64)nReturn / 32.0) * 32.0);
	}
#endif

	return nReturn;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

/*/
/ / bool
/ /		apiIsMultiMonitorSupported (void)
/ /
/ / PURPOSE:
/ /		Determines if the host environment supports more than monitor or not.
/ /
/ / RETURNS:
/ /		Returns true or false.
/*/

OATINLINE(bool)
apiIsMultiMonitorSupported (void)
{
	bool bReturn = false;
	OS osVersion = _GetOSVersion();

#if defined(_WIN32)

	// if we are running on Windows, multi monitors are only supported on Win98+ and Win2K+
	if(((osVersion >= Win98) && (osVersion < WinNT4)) || (osVersion >= Win2000)) bReturn = true;
#endif

	return bReturn;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////// PRIVATE ROUTINES /////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////

/*/
/ / Release                    Version                      File dates
/ / ------------------------------------------------------------------------
/ / Windows 95 retail, OEM     4.00.950                     7/11/95
/ / Windows 95 retail SP1      4.00.950A                    7/11/95-12/31/95
/ / OEM Service Release 2      4.00.1111* (4.00.950B)       8/24/96
/ / OEM Service Release 2.1    4.03.1212-1214* (4.00.950B)  8/24/96-8/27/97  
/ / OEM Service Release 2.5    4.03.1214* (4.00.950C)       8/24/96-11/18/97
/ / Windows 98 retail, OEM     4.10.1998                    5/11/98
/ / Windows 98, Security CD    4.10.1998A 
/ / Windows 98 Second Edition  4.10.2222A                   4/23/99
/ / Windows 98 SE Security CD  4.10.2222B
/ / Windows Me                 4.90.3000                    6/08/00
/ / Windows Me Security CD     4.90.3000A
/*/

/*/
/ / int32
/ /		ShowErrorMessage(const uint32 nTextID)
/ /
/ /		nTextID = id of the string resource to use in the message
/ /
/ / PURPOSE:
/ /		Simplify the usage of showing a message box with using resource identifiers for text.
/ /
/ / NOTES:
/ /		Assumes the resouce file belongs to the module that started the current process.
/*/

static OS
_GetOSVersion (void)
{
#if defined(_WIN32)
	OS osVersion = WinUnknown;
	OSVERSIONINFO osVersionInfo = {0};

	// let the API know how large the struct is to avoid memory corruption
	osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

	if(GetVersionEx(&osVersionInfo))
	{
		switch(osVersionInfo.dwPlatformId)
		{
			// test case for Windows NT-based (both 32 and 64-bit)
			case VER_PLATFORM_WIN32_NT:

				if((osVersionInfo.dwMajorVersion == 6) && (osVersionInfo.dwMinorVersion == 0))
					osVersion = WinVista;

				else if((osVersionInfo.dwMajorVersion == 5) && (osVersionInfo.dwMinorVersion == 2))
					osVersion = WinServer2003;

				else if((osVersionInfo.dwMajorVersion == 5) && (osVersionInfo.dwMinorVersion == 1))
					osVersion = WinXP;

				else if((osVersionInfo.dwMajorVersion == 5) && (osVersionInfo.dwMinorVersion == 0))
					osVersion = Win2000;

				else if((osVersionInfo.dwMajorVersion == 4) && (osVersionInfo.dwMinorVersion == 0))
					osVersion = WinNT4;

			// test case for Windows 9x
			case VER_PLATFORM_WIN32_WINDOWS:

				if((osVersionInfo.dwMajorVersion == 4) && (osVersionInfo.dwMinorVersion == 0))
				{
					if((osVersionInfo.szCSDVersion[1] == _T('C')) || (osVersionInfo.szCSDVersion[1] == _T('B')))
						osVersion = Win95OSR2;
					else
						osVersion = Win95;
				}

				else if((osVersionInfo.dwMajorVersion == 4) && (osVersionInfo.dwMinorVersion == 10))
				{
					if(osVersionInfo.szCSDVersion[1] == _T('A'))
						osVersion = Win98SE;
					else
						osVersion = Win98;
				}

				else if((osVersionInfo.dwMajorVersion == 4) && (osVersionInfo.dwMinorVersion == 90))
					osVersion = WinME;
				break;

			// test case for the bastard stepchild that is Win32s
			case VER_PLATFORM_WIN32s:
				osVersion = Win32s;
				break;
		}
	}

#elif defined(_MAC)
	OS osVersion = MacUnknown;
#endif

	return osVersion; 
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////